package game;

import game.Item.ItemType;
import game.buffovi.Buff;

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import GUI.ImageStorage;
import GUI.anim.Animation;
import GUI.anim.Animator;

import util.INIFile;
import util.INIGroup;
import util.Util;

public class Character implements Loadable, ActionListener, Cloneable
{
	final int CAPACITY_MULTIPLIER = 10,BASE_CAPACITY = 50, BASE_HEALTH = 100,HEALTH_MULTIPLIER = 50,BASE_MANA = 50,MANA_MULTIPLIER = 5, DEFAULT_DAMAGE = 3, DEFAULT_DEFENSE = 5,MOVEMENT_MULTIPLIER = 3;
	
	static ArrayList<Character> monsters;
	
	public class BattleImage 
	{
		public BufferedImage idle, attack, damage;
		String idleTag,attackTag,damageTag;
		
		public BattleImage(String[] nesto)
		{
			if(nesto == null) return;
			idleTag = nesto[0];
			attackTag = nesto[1];
			damageTag = nesto[2];
		}
		
		public void Init()
		{
			idle = ImageStorage.getImage(idleTag);
			attack = ImageStorage.getImage(attackTag);
			damage = ImageStorage.getImage(damageTag);
		}
	}
	
	ArrayList<ActionListener> actionListeners = new ArrayList<ActionListener>();
	BufferedImage battleImage;
	
	public int battleX,battleY;
	
	private int baseStrength,baseDexterity,baseIntelligence,baseVitality,baseSpeed;
	private int strength,dexterity,vitality,intelligence,speed;
	private float baseFireResistance,baseLightningResistance,baseColdResistance;
	private float fireResistance,lightningResistance,coldResistance;
	private int maxMovementPoints,movementPointsLeft,actionPoints,actionPointsRegen;
	
	public int getMovementPointsLeft() {
		return movementPointsLeft;
	}
	public int getMaxMovementPoints() {
		return maxMovementPoints;
	}

	private int level;
	private int posX,posY;
	private int exp;
	private int bonusPtsAttr;
	
	private int health,maxHealth;
	private int mana,maxMana;
	int gold;
	
	boolean isDead;
	
	Item weapon,armor,shield,helmet;
	
	public Item getWeapon() {
		return weapon;
	}
	public Item getArmor() {
		return armor;
	}
	public Item getShield() {
		return shield;
	}
	public Item getHelmet() {
		return helmet;
	}

	String name, tag, weaponTag,shieldTag,armorTag,helmetTag,battleImageTag;
	
	String animTagR,animTagL,animTagU,animTagD;
	Animation animationR,animationL,animationU,animationD;
	Animator animator;
	
	public BattleImage maljutka, macina, pritka, kamenica, sikira, kabal;
	
	ItemContainer inventory;
	//ArrayList<Item> inventory;
	ArrayList<Buff> buffs;
	
	TileMap map;
	public int pixX = 0, pixY = 0;
	
	public Character()
	{
		inventory = new ItemContainer();
		buffs = new ArrayList<Buff>();
		isDead = false;
		animator = new Animator();
		
		inventory.addActionListener(this);
	}
	
	void Die(Character killer)
	{
		if(killer == this)
		{
			System.out.println("WTF? Suicide");
		}
		System.out.println(killer.getName() + " has killed " + name);
		killer.addExperience(exp);
		NotifyListeners(this, "CharacterDeath");
	}
	
	public void addExperience(int ammount)
	{
		System.out.println(name + " gains " + ammount + " XP");
		exp += ammount;
		while(exp >= experienceCapFor(level + 1))
			gainLevel();
	}
	
	public void gainLevel()
	{
		level++;
		bonusPtsAttr += 5;
		System.out.println(name + " gained a level! Now level " + level);
		NotifyListeners(this, "CharacterGainLevel");
	}
	
	public static int experienceCapFor(int level)
	{
		return (int) (100 * Math.pow(2.0, level - 2));
	}
	
	public void BattleTurnActivated()
	{
		actionPoints = 0;
	}
	
	public void BattleTurnPassed()
	{
		DoBuffs();
		
		actionPoints += actionPointsRegen;
	}
	
	public void GlobalTurnPassed()
	{
		movementPointsLeft = maxMovementPoints;
	}
	
	public void MovementTurnPassed()
	{
		movementPointsLeft -= map.getTileAt(posX, posY).getMovPenalty();
	}
	
	public void Move(int x,int y)
	{
		if(map.inMap(x, y))
		{
			if(movementPointsLeft >= map.getTileAt(x, y).getMovPenalty() 
					&& !map.getTileAt(x, y).isObstacle())
			{
				posX = x;
				posY = y;
				
				movementPointsLeft -= map.getTileAt(x, y).getMovPenalty();
			}
		}
	}
	
	void DoBuffs()
	{
		for(int i = 0;i < buffs.size();i++)
		{

			if(this.isDead) break;
			
			if(buffs.get(i).isActive())
			{
				buffs.get(i).PerformBuff(this);
			}
			
		}
		
		for(int i = 0;i < buffs.size();i++)
		{
			if(!buffs.get(i).isActive()) 
			{
				buffs.remove(i);
				i --;
			}
		}
		
	}
	
	public void UnequipItem(Item item)
	{
		if(weapon == item) { weapon = null; CalculateAttributes(); }
		if(shield == item) { shield = null; CalculateAttributes(); }
		if(armor == item) { armor = null; CalculateAttributes(); }
		if(helmet == item) { helmet = null; CalculateAttributes(); }
		
		NotifyListeners(item, "UnequipItem");
	}
	
	public boolean AddToInventory(Item item)
	{
		if(inventory.CanAdd(item)) 
		{
			inventory.Add(item);
			CalculateAttributes();
			NotifyListeners(item, "ItemAddToInventory");
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public void DropItem(Item item)
	{
		try {
			UnequipItem(item);
			inventory.Remove(item);
			map.addItem(item, posX, posY);
			NotifyListeners(item, "DropItem");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void PickUpItem(Item item)
	{
		if(item.getxPos() == posX && item.getyPos() == posY)
		{
			if(AddToInventory(item))
				{
				map.removeItems(item);
				NotifyListeners(item, "PickUpItem");
				}
		}
	}
	
	public boolean isDead()
	{
		return health <= 0;
	}
	
	public String toString()
	{
		String nesto = "";
		
		nesto += "Name: " + name + "\n";
		nesto += "Strenght: " + strength + "\n";
		nesto += "Dexterity: " + dexterity + "\n";
		nesto += "Intelligence: " + intelligence + "\n";
		nesto += "Vitality: " + vitality + "\n";
		nesto += "Health: " + health + "\n";
		
		return nesto;
	}

	public String getName() 
	{
		return name;
	}

	public void setName(String name) 
	{
		this.name = name;
	}

	
	public boolean loadFromFile(INIGroup grupa) 
	{
		
		baseStrength = Integer.parseInt(grupa.vals.get("strength")) ;
		baseDexterity = Integer.parseInt(grupa.vals.get("dexterity"));
		baseIntelligence = Integer.parseInt(grupa.vals.get("intelligence"));
		baseVitality = Integer.parseInt(grupa.vals.get("vitality"));
		level = Integer.parseInt(grupa.vals.get("level"));
		exp = grupa.getInteger("exp", 0);
		if(exp == 0)
			exp = experienceCapFor(level);
		mana = Integer.parseInt(grupa.vals.get("mana"));
//		health = Integer.parseInt(grupa.vals.get("health"));
		tag = grupa.vals.get("tag");
		weaponTag = grupa.vals.get("weaponTag");
		shieldTag = grupa.vals.get("shieldTag");
		armorTag = grupa.vals.get("armorTag");
		helmetTag = grupa.vals.get("helmetTag");
		name = grupa.vals.get("name");
		baseSpeed = Integer.parseInt(grupa.vals.get("speed")); // FFS
		baseFireResistance = grupa.getInteger("fireResistance", 0);
		baseColdResistance = grupa.getInteger("coldResistance", 0);
		baseLightningResistance = grupa.getInteger("lightningResistance", 0);
		battleImageTag = grupa.getString("battleImage");
		animTagD = grupa.getString("animTagD");
		animTagU = grupa.getString("animTagU");
		animTagR = grupa.getString("animTagR");
		animTagL = grupa.getString("animTagL");
		
		macina = new BattleImage(grupa.getStringList("macinaImageTag"));
		maljutka = new BattleImage(grupa.getStringList("maljutkaImageTag"));
		kamenica = new BattleImage(grupa.getStringList("kamenicaImageTag"));
		kabal = new BattleImage(grupa.getStringList("KabalImageTag"));
		pritka = new BattleImage(grupa.getStringList("pritkaImageTag"));
		sikira = new BattleImage(grupa.getStringList("sikiraImageTag"));
		
		CalculateAttributes();
		health = maxHealth;
		
		return false;
	}
	
	public String getNameHP()
	{
		return name + " (" + health + "/" + maxHealth + ")";
	}
	
	public boolean UseItem(Item item)
	{
		if(!inventory.Contains(item)) return false;
		
		ArrayList<MagicEffect> effects = item.getEffects();
		
		for(MagicEffect e:effects)
		{
			if(e.getStage() == EffectStage.CasterOnUse)
			{
				e.doEffect(this, null);
			}
		}
		
		if(item.type == ItemType.POTION) inventory.Remove(item);
		
		return true;
		
	}
	
	public boolean EquipItem(Item item)
	{	
		if(!inventory.Contains(item)) return false;
		
		if(item.getType() == ItemType.WEAPON)
		{	
			if(item.twoHanded && shield!=null)
			{
				return false;
			}
			
			weapon = item;
			CalculateAttributes();
			NotifyListeners(item, "EquipItem");
			return true;
		}
		else if(item.getType() == ItemType.SHIELD)
		{
			if(weapon!= null)
			{
				if(!weapon.twoHanded)
				{
					shield = item;
					CalculateAttributes();
					NotifyListeners(item, "EquipItem");
				}
				else {
					return false;
				}
			}
			else 
			{
				shield = item;
				CalculateAttributes();
				NotifyListeners(item, "EquipItem");
			}
		}
		else if(item.getType() == ItemType.ARMOR)
		{
			armor = item;
			CalculateAttributes();
			NotifyListeners(item, "EquipItem");
			return true;
		}
		else if(item.getType() == ItemType.HELMET)
		{
			helmet = item;
			CalculateAttributes();
			NotifyListeners(item, "EquipItem");
			return true;
		}
		else if(item.getType() == ItemType.POTION)
		{
			DoEffectsOnSelf(item);
			inventory.Remove(item);
			NotifyListeners(item, "EquipItem");
			return true;
		}
		return false;
	
	}
	
	public ItemContainer getInventory()
	{
		return inventory;
	}
	
	public void CalculateAttributes()
	{
		strength = baseStrength;
		dexterity = baseDexterity;
		intelligence = baseIntelligence;
		vitality = baseVitality;
		speed = baseSpeed;
		
		fireResistance = baseFireResistance;
		coldResistance = baseColdResistance;
		lightningResistance = baseLightningResistance;
		
		ArrayList<Item> inv = inventory.GetItemList();
		
		for(Item i:inv)
		{
			if(i.getType() == Item.ItemType.CHARM)
			{
			DoEffectsOnSelf(i,EffectStage.PreConstantEffect);
			}
		}
		
		for(Item i:inv)
		{
			if(i.getType() == Item.ItemType.CHARM)
			{
			DoEffectsOnSelf(i,EffectStage.ConstantEffect);
			}
		}
		
		if (weapon!=null)DoEffectsOnSelf(weapon,EffectStage.PreConstantEffect);
		if(armor!=null)DoEffectsOnSelf(armor,EffectStage.PreConstantEffect);
		if(shield!=null)DoEffectsOnSelf(shield,EffectStage.PreConstantEffect);
		if(helmet!=null)DoEffectsOnSelf(helmet,EffectStage.PreConstantEffect);
		
		if (weapon!=null)DoEffectsOnSelf(weapon,EffectStage.ConstantEffect);
		if(armor!=null)DoEffectsOnSelf(armor,EffectStage.ConstantEffect);
		if(shield!=null)DoEffectsOnSelf(shield,EffectStage.ConstantEffect);
		if(helmet!=null)DoEffectsOnSelf(helmet,EffectStage.ConstantEffect);
		
		inventory.setMaxWeight(BASE_CAPACITY + CAPACITY_MULTIPLIER * strength);
		maxHealth = BASE_HEALTH + HEALTH_MULTIPLIER * vitality;
		maxMana = BASE_MANA + MANA_MULTIPLIER * intelligence;
		maxMovementPoints = speed * MOVEMENT_MULTIPLIER;
		actionPointsRegen = speed;
	}
	
	public boolean Attack(Character character)
	{
		if(character == this)
		{
			System.out.println(name + " trying to attack itself!");
			return false;
		}
		
		int damage = DEFAULT_DAMAGE * strength;

		if (Util.getRndInt(0, dexterity * 3) > Util.getRndInt(0,character.getDexterity())) 
		{

			if (weapon != null) 
			{
				if (weapon.ranged) 
				{
					NotifyListeners(character, "AttackHit");
					damage += Util.getRndInt(weapon.getMinDamage(),weapon.getMaxDamage())* (dexterity / 2);
					System.out.println(name + " attacks " + character.name + " with " + weapon.getName() + "\n");
					character.SufferDamage(damage,DamageType.Physical,this);
					DoEffectsOnTarget(weapon, character, EffectStage.CasterOnStrike);
					DoEffectsOnTarget(weapon,character,EffectStage.TargetOnStrike);
					return false;
				} 
				else 
				{
					NotifyListeners(character, "AttackHit");
					damage += Util.getRndInt(weapon.getMinDamage(),
							weapon.getMaxDamage())
							* (strength / 2);
					System.out.println(name + " attacks " + character.name + " with " + weapon.getName() + "\n");
					character.SufferDamage(damage,DamageType.Physical,this);
					DoEffectsOnTarget(weapon, character, EffectStage.CasterOnStrike);
					DoEffectsOnTarget(weapon,character,EffectStage.TargetOnStrike);
					return true;
				}
				
			}
			NotifyListeners(character, "AttackHit");
			character.SufferDamage(damage,DamageType.Physical,this);
			return true;
			
		} 
		else
		{	
			System.out.println(name + " misses");
			if(weapon != null) 
			{// FFS
				NotifyListeners(character, "AttackMiss");
				return !weapon.ranged;
			}
			else
				return false;
		}
	}
	
	public int getMinDamage()
	{	
		if(weapon != null)
		{
			if(weapon.ranged)
			{
				return weapon.minDamage * (dexterity / 2);
			}
			else {
				return weapon.minDamage * (strength/2);
			}
		}
		else {
			return DEFAULT_DAMAGE * strength;
		}
	}
	
	public int getMaxDamage()
	{
		if(weapon != null)
		{
			if(weapon.ranged)
			{
				return weapon.maxDamage * (dexterity / 2);
			}
			else {
				return weapon.maxDamage * (strength/2);
			}
		}
		else {
			return DEFAULT_DAMAGE * strength;
		}
	}
	
	public int SufferDamage(int damage,DamageType type,Character attacker)
	{
		if(type == DamageType.Physical)
		{
			damage -= DEFAULT_DEFENSE;
			
			if(shield != null)
			{
				damage -= shield.armor;
				System.out.println(name + " blocks " + shield.armor + " damage with his shield \n");
			}
			
			if(armor != null) 
			{
				damage -= armor.armor;
				System.out.println(name + " blocks " + armor.armor + " damage with his armor \n");
			}
			
			if(helmet != null) 
			{
				damage -= helmet.armor;
				System.out.println(name + " blocks " + helmet.armor + " damage with his helmet \n");
			}
		}
		else if(type == DamageType.Cold)
		{
			damage -= (int)(damage * (coldResistance / 100.0f)); 
		}
		else if(type == DamageType.Fire)
		{
			damage -= (int)(damage * (fireResistance / 100.0f));
		}
		else if(type == DamageType.Lightning)
		{
			damage -= (int)(damage * (lightningResistance / 100.0f));
		}
		
		if(damage < 0) damage = 0;
		System.out.println(name + " receives " + damage + " " + type.toString().toLowerCase() + " damage \n" );
		
		health -= damage;
		
		if (health <= 0 && !isDead) 
		{
			Die(attacker);
			isDead = true;
			buffs.clear();
		}
		return damage;
	}
	
	public boolean CastSpell(Spell spell,Character target)
	{
		return spell.cast(this, target);
	}
	
	public boolean canCastSpell(Spell spell)
	{
		return spell.getManaCost() <= this.mana;
	}
	
	void DoEffectsOnSelf(Item item)
	{
		ArrayList <MagicEffect> effects = item.getEffects();
		
		for(MagicEffect e:effects)
		{
			e.doEffect(this,null);
		}
	}
	
	void DoEffectsOnSelf(Item item,EffectStage stage)
	{
		ArrayList <MagicEffect> effects = item.getEffects();
		
		for(MagicEffect e:effects)
		{
			if(e.getStage() == stage) 
			{
				e.doEffect(this, null);
			}
		}
	}
	
	void DoEffectsOnTarget(Item item,Character target,EffectStage stage)
	{
		ArrayList <MagicEffect> effects = item.getEffects();
		
		for(MagicEffect e:effects)
		{
			if(e.getStage() == stage)
			{
				e.doEffect(this, target);
			}
		}
	}
	
	public void BattleEnd()
	{
		buffs.clear();
	}
	
	public int getBaseStrength() {
		return baseStrength;
	}

	public void setBaseStrength(int baseStrength) 
	{
		if(baseStrength < 0)
		{
			this.baseStrength = 0;
		}
		else this.baseStrength = baseStrength;
	}

	public int getBaseDexterity() {
		return baseDexterity;
	}

	public void setBaseDexterity(int baseDexterity) 
	{
		if(baseDexterity < 0)
		{
			this.baseDexterity = 0;
		}
		else this.baseDexterity = baseDexterity;
	}

	public int getBaseIntelligence() 
	{
		return baseIntelligence;
	}

	public void setBaseIntelligence(int baseIntelligence) 
	{
		if(baseIntelligence < 0)
		{
			this.baseIntelligence = 0;
		}
		else this.baseIntelligence = baseIntelligence;
	}

	public int getBaseVitality() {
		return baseVitality;
	}

	public void setBaseVitality(int baseVitality) 
	{
		if(baseVitality < 0)
		{
			this.baseVitality = 0;
		}
		else this.baseVitality = baseVitality;
	}

	public int getStrength() {
		return strength;
	}

	public void setStrength(int strength) 
	{
		this.strength = strength;
	}

	public int getDexterity() {
		return dexterity;
	}

	public void setDexterity(int dexterity) {
		this.dexterity = dexterity;
	}

	public int getVitality() {
		return vitality;
	}

	public void setVitality(int vitality) 
	{
		this.vitality = vitality;
	}

	public int getIntelligence() 
	{
		return intelligence;
	}

	public void setIntelligence(int intelligence) 
	{
		this.intelligence = intelligence;
	}

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) 
	{
		if(health > maxHealth)
		{
			health = maxHealth;
		}
		else 
		{
		this.health = health;
		}
		if(isDead()) System.out.println("Ubi charactera iz setHealth");
	}

	public int getMaxHealth() 
	{
		return maxHealth;
	}

	public void setMaxHealth(int maxHealth) 
	{
		this.maxHealth = maxHealth;
	}

	public int getMana() {
		return mana;
	}

	public void setMana(int mana) 
	{
		if(mana > maxMana) {
			this.mana = maxMana;
		}
		else 
		{
			this.mana = mana;
		}
	}

	public int getMaxMana() {
		return maxMana;
	}

	public void setMaxMana(int maxMana) {
		this.maxMana = maxMana;
	}
	
	public String getTag()
	{
		return tag;
	}
	
	public static void LoadMonsters()
	{
		try {
			INIFile ini = new INIFile("Beshtije.txt");
			INIGroup g = null;
			while((g = ini.getNext()) != null)
			{
				Character c = new Character();
				c.loadFromFile(g);
				monsters.add(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	public Character Clone()
	{
		Character nesto = new Character();
		
		nesto.strength = strength;
		nesto.dexterity = dexterity;
		nesto.intelligence = intelligence;
		nesto.vitality = vitality;
		
		return nesto;
	}
	
	public static Character GenerateMonster(int level) 
	{
		for(Character c:monsters)
		{
			if(c.level == level)
			{
				Character novi = c.Clone();
				novi.AddToInventory(Item.genItem(level));
				return novi;
			}
		}
		
		return null;
	}

	public float getBaseFireResistance() {
		return baseFireResistance;
	}

	public void setBaseFireResistance(int baseFireResistance) {
		this.baseFireResistance = baseFireResistance;
	}

	public float getBaseLightingResistance() {
		return baseLightningResistance;
	}

	public void setBaseLightingResistance(int baseLightingResistance) {
		this.baseLightningResistance = baseLightingResistance;
	}

	public float getBaseColdResistance() {
		return baseColdResistance;
	}

	public void setBaseColdResistance(int baseColdResistance) {
		this.baseColdResistance = baseColdResistance;
	}

	public float getFireResistance() {
		return fireResistance;
	}

	public void setFireResistance(int fireResistance) {
		this.fireResistance = fireResistance;
	}

	public float getLightningResistance() {
		return lightningResistance;
	}

	public void setLightningResistance(int lightingResistance) {
		this.lightningResistance = lightingResistance;
	}

	public float getColdResistance() {
		return coldResistance;
	}

	public void setColdResistance(int coldResistance) {
		this.coldResistance = coldResistance;
	}

	public void setMap(TileMap map)
	{
		this.map = map;
	}
	
	public void init() 
	{
		weapon = GameBase.instItemByTag(weaponTag);
		armor = GameBase.instItemByTag(armorTag);
		shield = GameBase.instItemByTag(shieldTag);
		helmet = GameBase.instItemByTag(helmetTag);
		battleImage = ImageStorage.getImage(battleImageTag);
		
		animationD = GameBase.getAnimationByTag(animTagD);
		animationU = GameBase.getAnimationByTag(animTagU);
		animationL = GameBase.getAnimationByTag(animTagL);
		animationR = GameBase.getAnimationByTag(animTagR);
		animator.setAnimation(animationD);
		
		sikira.Init();
		pritka.Init();
		macina.Init();
		pritka.Init();
		kamenica.Init();
		kabal.Init();
		maljutka.Init();

	}
	
	public void AddBuff(Buff buff)
	{
		buffs.add(buff);
	}

	public int getPosX() {
		return posX;
	}

	public void setPosX(int posX) {
		this.posX = posX;
	}

	public int getPosY() {
		return posY;
	}

	public void setPosY(int posY) {
		this.posY = posY;
	}

	public int getGold() {
		return gold;
	}

	public void setGold(int gold) {
		this.gold = gold;
	}

	public int getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}
	
	public int getActionPoints() {
		return actionPoints;
	}
	
	public int getExp() {
		return exp;
	}
	
	public void AddActionListener(ActionListener listener)
	{
		actionListeners.add(listener);
	}
	
	public void RemoveActionListener(ActionListener listener)
	{
		actionListeners.remove(listener);
	}
	
	public void NotifyListeners(Object source,String action)
	{
		ActionEvent nesto = new ActionEvent(source, 0, action);
		
		for(int i = 0; i < actionListeners.size();i++)
		{
			actionListeners.get(i).actionPerformed(nesto);
		}
	}

	public Animation getAnimationR() {
		return animationR;
	}

	public Animation getAnimationL() {
		return animationL;
	}


	public Animation getAnimationU() {
		return animationU;
	}

	public Animation getAnimationD() {
		return animationD;
	}

	public Animator getAnimator() {
		return animator;
	}

	public Point getPos() {
		return new Point(posX, posY);
	}
	
	public void assignAnimation(int dx, int dy)
	{
		if(dx == 1 && dy == 0) { animator.setAnimation(animationR); return; }
		if(dx == -1 && dy == 0) { animator.setAnimation(animationL); return; }
		if(dx == 0 && dy == -1) { animator.setAnimation(animationU); return; }
		animator.setAnimation(animationD);
	}
	@Override
	public void actionPerformed(ActionEvent e)
	{
		if("item-remove".equals(e.getActionCommand()))
		{
			if(e.getSource() instanceof Item)
			{
				UnequipItem((Item)e.getSource());
			}
		}
	}
	
	public Object clone()
    {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
    } 
}
